<!-- CopyRight (C) 2017-2022 Alibaba Group Holding Limited. -->
<!-- Created by Tw93 on 17/07/28. -->

<template>
  <div :style="containerS">
    <div :ref="`sliderCtn_${sliderId}`"
         :style="{width:cardS.width+'px',height:cardHeight+'px',transform: `translateY(-${currentIndex * (cardS.height + cardS.spacing)}px)`}"
         @panstart="onPanStart"
         @panmove="onPanMove"
         @panend="onPanEnd">
      <div class="slider"
           v-for="(v, index) in cardList"
           v-bind:key="index"
           :ref="`card${index}_${sliderId}`"
           :style="{transform: `scale(${index===currentIndex ? 1 : cardS.scale})`,top: `${index * (cardS.height+cardS.spacing)}px`,marginTop:`${(containerS.height - cardS.height) / 2}px`,width: cardS.width+'px', height: cardS.height+'px'}">
        <slot :name="`card${index}_${sliderId}`" v-if="index > currentIndex - 3 && index < currentIndex + 3"></slot>
      </div>
    </div>
  </div>
</template>

<style scoped>
  .slider {
    position: absolute;
    top: 0;
  }
</style>

<script>
import { Utils, BindEnv } from 'weex-ui'
import Binding from 'weex-bindingx/lib/index.weex.js'
const swipeBack = weex.requireModule('swipeBack')
const animation = weex.requireModule('animation')

export default {
  props: {
    sliderId: {
      type: [String, Number],
      default: 1
    },
    panOffset: {
      type: Number,
      default: 300
    },
    cardLength: {
      type: Number,
      default: 1
    },
    selectIndex: {
      type: Number,
      default: 0
    },
    enableSwipe: {
      type: Boolean,
      default: true
    },
    containerS: {
      type: Object,
      default: () => ({
        position: 'relative',
        width: 750,
        height: 352
      })
    },
    cardS: {
      type: Object,
      default: () => ({
        width: 360,
        height: 300,
        spacing: 0,
        scale: 0.75
      })
    },
    autoPlay: {
      type: Boolean,
      default: false
    },
    interval: {
      type: [Number, String],
      default: 1200
    }
  },
  data: () => ({
    isMoving: false,
    gesToken: 0,
    startY: 0,
    startTime: 0,
    currentIndex: 0,
    autoPlayTimer: null
  }),
  computed: {
    cardList () {
      return new Array(this.cardLength + 1).join().split('')
    },
    cardHeight () {
      return (this.cardLength - 1) * this.cardS.height + this.containerS.height + 235
    }
  },
  created () {
    this.currentIndex = this.selectIndex
  },
  mounted () {
    // ios和页面返回冲突，组件里面将ios系统横滑返回禁止
    if (swipeBack && swipeBack.forbidSwipeBack) {
      swipeBack.forbidSwipeBack(true)
    }

    setTimeout(() => {
      const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]
      if (BindEnv.supportsEB() && sliderCtn && sliderCtn.ref) {
        Binding.prepare && Binding.prepare({
          anchor: sliderCtn.ref,
          eventType: 'pan'
        })
      }
    }, 20)
    this.checkNeedAutoPlay()
  },
  methods: {
    onPanStart (e) {
      if (BindEnv.supportsEB()) {
        this.clearAutoPlay()
        setTimeout(() => {
          const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]
          this.bindExp(sliderCtn)
        }, 0)
        return
      }
      this.clearAutoPlay()
      this.startY = e.changedTouches[0].clientY
      this.startTime = Date.now()
    },
    onPanMove (e) {
      if (BindEnv.supportsEB()) {
        return
      }
      const moveY = e.changedTouches[0].clientY - this.startY
      const index = this.loopedIndex(this.currentIndex, this.cardLength)
      const cardLength = this.cardLength
      const currentCardTop = this.currentIndex * (this.cardS.height + this.cardS.spacing)

      const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]
      sliderCtn && animation.transition(sliderCtn, {
        styles: {
          transform: `translateY(${moveY - currentCardTop}px)`
        },
        timingFunction: 'ease',
        delay: 0,
        duration: 0
      }, () => {
      })

      if (this.cardS.scale !== 1) {
        const currentCard = this.$refs[`card${this.loopedIndex(index, cardLength)}_${this.sliderId}`][0]
        currentCard && animation.transition(currentCard, {
          styles: {
            transform: `scale(${1 - Math.abs(moveY) / (this.cardS.height) * (1 - this.cardS.scale)})`
          },
          timingFunction: 'ease',
          delay: 0,
          duration: 0
        }, () => {
        })
        // 左边的卡片
        const leftCard = this.$refs[`card${this.loopedIndex(index - 1, cardLength)}_${this.sliderId}`][0]
        // loop 函数负数返回 0，这里有点冲突
        if (leftCard && index !== 0) {
          animation.transition(leftCard, {
            styles: {
              transform: `scale(${1 - Math.abs(moveY - this.cardS.height) / (this.cardS.height) * (1 - this.cardS.scale)})`
            },
            timingFunction: 'ease',
            delay: 0,
            duration: 0
          }, () => {
          })
        }
        // 右边卡片
        const rightCard = this.$refs[`card${this.loopedIndex(index + 1, cardLength)}_${this.sliderId}`][0]
        rightCard && animation.transition(rightCard, {
          styles: {
            transform: `scale(${1 - Math.abs(this.cardS.height + moveY) / (this.cardS.height) * (1 - this.cardS.scale)})`
          },
          timingFunction: 'ease',
          delay: 0,
          duration: 0
        }, () => {
        })
      }
    },
    onEpPanStart (e) {
      if (BindEnv.supportsEB() && e.state === 'start') {
        this.clearAutoPlay()
        setTimeout(() => {
          const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]
          this.bindExp(sliderCtn)
        }, 0)
      }
    },
    onPanEnd (e) {
      if (BindEnv.supportsEB()) {
        return
      }
      this.panEnd(e)
    },
    panEnd (e) {
      this.isMoving = true
      let moveY = e.deltaY

      if (Utils.env.isWeb()) {
        moveY = e.changedTouches[0].clientY - this.startY
      }

      const originIndex = this.currentIndex
      let selectIndex = originIndex
      const duration = Date.now() - this.startTime
      const panOffset = this.panOffset || (this.cardS.height / 2)

      if (moveY < -panOffset || (moveY < -100 && duration < 200)) {
        if (selectIndex !== this.cardLength - 1) {
          selectIndex++
        }
      } else if (moveY > panOffset || (moveY > 100 && duration < 500)) {
        if (selectIndex !== 0) {
          selectIndex--
        }
      }

      this.slideTo(originIndex, selectIndex)
      setTimeout(() => { this.checkNeedAutoPlay() }, 3000)
    },
    sliderToIndex (index) {
      this.slideTo(this.currentIndex, index)
    },
    slideTo (originIndex, selectIndex) {
      let currentCardScale = 1
      let rightCardScale = this.cardS.scale
      let leftCardScale = this.cardS.scale
      const duration = (selectIndex === 0 && originIndex === this.cardLength - 1 && this.cardLength !== 2) ? 0.00001 : 300
      this.$emit('wxcEpSliderCurrentIndexSelected', { currentIndex: selectIndex })
      if (originIndex < selectIndex) {
        currentCardScale = this.cardS.scale
        rightCardScale = 1
      } else if (originIndex > selectIndex) {
        currentCardScale = this.cardS.scale
        leftCardScale = 1
      }
      const currentCard = this.$refs[`card${this.loopedIndex(originIndex, this.cardLength)}_${this.sliderId}`][0]
      currentCard && animation.transition(currentCard, {
        styles: {
          transform: `scale(${currentCardScale})`
        },
        timingFunction: 'ease',
        duration
      }, () => {
      })

      const leftCard = this.$refs[`card${this.loopedIndex(originIndex - 1, this.cardLength)}_${this.sliderId}`][0]
      if (this.isMoving && leftCard && originIndex !== 0) {
        animation.transition(leftCard, {
          styles: {
            transform: `scale(${leftCardScale})`
          },
          timingFunction: 'ease',
          duration
        }, () => {
        })
      }
      const rightCard = this.$refs[`card${this.loopedIndex(originIndex + 1, this.cardLength)}_${this.sliderId}`][0]
      if (rightCard && originIndex !== this.cardLength - 1) {
        animation.transition(rightCard, {
          styles: {
            transform: `scale(${rightCardScale})`
          },
          timingFunction: 'ease',
          duration
        }, () => {
        })
      }

      const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]
      sliderCtn && animation.transition(sliderCtn, {
        styles: {
          transform: `translateY(-${selectIndex * (this.cardS.height + this.cardS.spacing)}px)`
        },
        timingFunction: 'ease',
        duration
      }, () => {
        this.isMoving = false
        if (originIndex !== selectIndex) {
          this.currentIndex = selectIndex
        }
      })
    },
    // 使index维持在0-length之间循环
    loopedIndex (index, total) {
      if (index < 0) {
        index = index + (1 - index / total) * total
      }
      return index % total
    },
    bindExp (element) {
      if (element && element.ref) {
        if (this.isMoving && this.gesToken !== 0) {
          Binding.unbind({
            eventType: 'pan',
            token: this.gesToken
          })
          this.gesToken = 0
          return
        }

        this.startTime = Date.now()
        const index = this.loopedIndex(this.currentIndex, this.cardLength)
        const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]
        const currentCard = this.$refs[`card${index}_${this.sliderId}`][0]
        let rightCard = null
        let leftCard = null
        const currentCardLeft = this.currentIndex * (this.cardS.height + this.cardS.spacing)

        // 卡片容器
        const sliderCtnExp = `y - ${currentCardLeft}`
        const args = [
          {
            element: sliderCtn.ref,
            property: 'transform.translateY',
            expression: sliderCtnExp
          }
        ]

        if (this.cardS.scale !== 1) {
          const currentCardExp = `1-abs(y)/${this.cardS.height}*${1 - this.cardS.scale}`
          const leftCardExp = `1-abs(y-${this.cardS.height})/${this.cardS.height}*${1 - this.cardS.scale}`
          const rightCardExp = `1-abs(${this.cardS.height}+y)/${this.cardS.height}*${1 - this.cardS.scale}`

          args.push({
            element: currentCard.ref,
            property: 'transform.scale',
            expression: currentCardExp
          })

          if (index === 0 && this.$refs[`card${index + 1}_${this.sliderId}`]) {
            rightCard = this.$refs[`card${index + 1}_${this.sliderId}`][0]
            args.push({
              element: rightCard.ref,
              property: 'transform.scale',
              expression: rightCardExp
            })
          } else if (index === this.cardLength - 1 && this.$refs[`card${index - 1}_${this.sliderId}`]) {
            leftCard = this.$refs[`card${index - 1}_${this.sliderId}`][0]
            args.push({
              element: leftCard.ref,
              property: 'transform.scale',
              expression: leftCardExp
            })
          } else if (this.$refs[`card${index - 1}_${this.sliderId}`]) {
            // 左边卡片
            leftCard = this.$refs[`card${index - 1}_${this.sliderId}`][0]
            args.push({
              element: leftCard.ref,
              property: 'transform.scale',
              expression: leftCardExp
            })
            // 右边卡片
            rightCard = this.$refs[`card${index + 1}_${this.sliderId}`][0]
            args.push({
              element: rightCard.ref,
              property: 'transform.scale',
              expression: rightCardExp
            })
          }
        }

        const gesTokenObj = Binding.bind({
          anchor: element.ref,
          eventType: 'pan',
          props: args
        }, (e) => {
          if (!this.isMoving && (e.state === 'end' || e.state === 'cancel' || e.state === 'exit')) {
            this.panEnd(e)
          }
        })

        this.gesToken = gesTokenObj.token
      }
    },
    checkNeedAutoPlay () {
      if (this.autoPlay) {
        this.clearAutoPlay()
        this.autoPlayTimer = setInterval(() => {
          this.slideTo(this.currentIndex, this.loopedIndex(this.currentIndex + 1, this.cardLength))
        }, parseInt(this.interval))
      }
    },
    clearAutoPlay () {
      this.autoPlayTimer && clearInterval(this.autoPlayTimer)
    },
    rebind () {
      const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]
      if (sliderCtn && sliderCtn.ref) {
        Binding.unbind({
          eventType: 'pan',
          token: this.gesToken
        })
        this.gesToken = 0
        this.bindExp(sliderCtn)
      }
    },
    manualSetPage (selectIndex) {
      this.clearAutoPlay()
      const step = this.currentIndex < selectIndex ? 1 : -1
      this.slideTo(this.loopedIndex(selectIndex - step, this.cardLength), selectIndex)
      setTimeout(() => {
        this.checkNeedAutoPlay()
      }, 3000)
    }
  }
}
</script>
